; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=instsimplify < %s | FileCheck %s

declare void @bar(ptr %a, ptr nonnull noundef %b)
declare void @bar_without_noundef(ptr %a, ptr nonnull %b)

; 'y' must be nonnull.

define i1 @caller1(ptr %x, ptr %y) {
; CHECK-LABEL: @caller1(
; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y:%.*]])
; CHECK-NEXT:    ret i1 false
;
  call void @bar(ptr %x, ptr %y)
  %null_check = icmp eq ptr %y, null
  ret i1 %null_check
}

; Don't know anything about 'y'.

define i1 @caller1_maybepoison(ptr %x, ptr %y) {
; CHECK-LABEL: @caller1_maybepoison(
; CHECK-NEXT:    call void @bar_without_noundef(ptr [[X:%.*]], ptr [[Y:%.*]])
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y]], null
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
;
  call void @bar_without_noundef(ptr %x, ptr %y)
  %null_check = icmp eq ptr %y, null
  ret i1 %null_check
}

; Don't know anything about 'y'.

define i1 @caller2(ptr %x, ptr %y) {
; CHECK-LABEL: @caller2(
; CHECK-NEXT:    call void @bar(ptr [[Y:%.*]], ptr [[X:%.*]])
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y]], null
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
;
  call void @bar(ptr %y, ptr %x)
  %null_check = icmp eq ptr %y, null
  ret i1 %null_check
}

; 'y' must be nonnull.

define i1 @caller3(ptr %x, ptr %y) {
; CHECK-LABEL: @caller3(
; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y:%.*]])
; CHECK-NEXT:    ret i1 true
;
  call void @bar(ptr %x, ptr %y)
  %null_check = icmp ne ptr %y, null
  ret i1 %null_check
}

; FIXME: The call is guaranteed to execute, so 'y' must be nonnull throughout.

define i1 @caller4(ptr %x, ptr %y) {
; CHECK-LABEL: @caller4(
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp ne ptr [[Y:%.*]], null
; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y]])
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
;
  %null_check = icmp ne ptr %y, null
  call void @bar(ptr %x, ptr %y)
  ret i1 %null_check
}

; The call to bar() does not dominate the null check, so no change.

define i1 @caller5(ptr %x, ptr %y) {
; CHECK-LABEL: @caller5(
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y:%.*]], null
; CHECK-NEXT:    br i1 [[NULL_CHECK]], label [[T:%.*]], label [[F:%.*]]
; CHECK:       t:
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
; CHECK:       f:
; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y]])
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
;
  %null_check = icmp eq ptr %y, null
  br i1 %null_check, label %t, label %f
t:
  ret i1 %null_check
f:
  call void @bar(ptr %x, ptr %y)
  ret i1 %null_check
}

; Make sure that an invoke works similarly to a call.

declare i32 @esfp(...)

define i1 @caller6(ptr %x, ptr %y) personality ptr @esfp{
; CHECK-LABEL: @caller6(
; CHECK-NEXT:    invoke void @bar(ptr [[X:%.*]], ptr nonnull [[Y:%.*]])
; CHECK-NEXT:    to label [[CONT:%.*]] unwind label [[EXC:%.*]]
; CHECK:       cont:
; CHECK-NEXT:    ret i1 false
; CHECK:       exc:
; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT:    filter [0 x ptr] zeroinitializer
; CHECK-NEXT:    unreachable
;
  invoke void @bar(ptr %x, ptr nonnull %y)
  to label %cont unwind label %exc

cont:
  %null_check = icmp eq ptr %y, null
  ret i1 %null_check

exc:
  %lp = landingpad { ptr, i32 }
  filter [0 x ptr] zeroinitializer
  unreachable
}

declare ptr @returningPtr(ptr returned %p)

define i1 @nonnullReturnTest(ptr nonnull %x) {
; CHECK-LABEL: @nonnullReturnTest(
; CHECK-NEXT:    [[X2:%.*]] = call ptr @returningPtr(ptr [[X:%.*]])
; CHECK-NEXT:    ret i1 false
;
  %x2 = call ptr @returningPtr(ptr %x)
  %null_check = icmp eq ptr %x2, null
  ret i1 %null_check
}

define i1 @unknownReturnTest(ptr %x) {
; CHECK-LABEL: @unknownReturnTest(
; CHECK-NEXT:    [[X2:%.*]] = call ptr @returningPtr(ptr [[X:%.*]])
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[X2]], null
; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
;
  %x2 = call ptr @returningPtr(ptr %x)
  %null_check = icmp eq ptr %x2, null
  ret i1 %null_check
}

; Make sure that if load/store happened, the pointer is nonnull.

define i32 @test_null_after_store(ptr %0) {
; CHECK-LABEL: @test_null_after_store(
; CHECK-NEXT:    store i32 123, ptr [[TMP0:%.*]], align 4
; CHECK-NEXT:    ret i32 2
;
  store i32 123, ptr %0, align 4
  %2 = icmp eq ptr %0, null
  %3 = select i1 %2, i32 1, i32 2
  ret i32 %3
}

define i32 @test_null_after_load(ptr %0) {
; CHECK-LABEL: @test_null_after_load(
; CHECK-NEXT:    ret i32 1
;
  %2 = load i32, ptr %0, align 4
  %3 = icmp eq ptr %0, null
  %4 = select i1 %3, i32 %2, i32 1
  ret i32 %4
}

; Make sure that different address space does not affect null pointer check.

define i32 @test_null_after_store_addrspace(ptr addrspace(1) %0) {
; CHECK-LABEL: @test_null_after_store_addrspace(
; CHECK-NEXT:    store i32 123, ptr addrspace(1) [[TMP0:%.*]], align 4
; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq ptr addrspace(1) [[TMP0]], null
; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i32 1, i32 2
; CHECK-NEXT:    ret i32 [[TMP3]]
;
  store i32 123, ptr addrspace(1) %0, align 4
  %2 = icmp eq ptr addrspace(1) %0, null
  %3 = select i1 %2, i32 1, i32 2
  ret i32 %3
}

define i32 @test_null_after_load_addrspace(ptr addrspace(1) %0) {
; CHECK-LABEL: @test_null_after_load_addrspace(
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr addrspace(1) [[TMP0:%.*]], align 4
; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq ptr addrspace(1) [[TMP0]], null
; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 1
; CHECK-NEXT:    ret i32 [[TMP4]]
;
  %2 = load i32, ptr addrspace(1) %0, align 4
  %3 = icmp eq ptr addrspace(1) %0, null
  %4 = select i1 %3, i32 %2, i32 1
  ret i32 %4
}

; Make sure if store happened after the check, nullptr check is not removed.

declare ptr @func(i64)

define ptr @test_load_store_after_check(ptr %0) {
; CHECK-LABEL: @test_load_store_after_check(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @func(i64 0)
; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[TMP1]], null
; CHECK-NEXT:    br i1 [[NULL_CHECK]], label [[RETURN:%.*]], label [[IF_END:%.*]]
; CHECK:       if.end:
; CHECK-NEXT:    store i8 7, ptr [[TMP1]], align 1
; CHECK-NEXT:    br label [[RETURN]]
; CHECK:       return:
; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi ptr [ [[TMP1]], [[IF_END]] ], [ null, [[ENTRY:%.*]] ]
; CHECK-NEXT:    ret ptr [[RETVAL_0]]
;
entry:
  %1 = call ptr @func(i64 0)
  %null_check = icmp eq ptr %1, null
  br i1 %null_check, label %return, label %if.end

if.end:
  store i8 7, ptr %1
  br label %return

return:
  %retval.0 = phi ptr [ %1, %if.end ], [ null, %entry ]
  ret ptr %retval.0
}
